<?php
/**
 * Created by Error202
 * Date: 15.08.2017
 */

namespace app\core\services;

use DomainException;
use Exception;
use ReflectionException;
use yii\rbac\ManagerInterface;
use yii\helpers\Json;
use yii\helpers\ArrayHelper;
use yii\rbac\Permission;

class PermissionManager
{
    private ManagerInterface $manager;

    public function __construct(ManagerInterface $manager)
    {
        $this->manager = $manager;
    }

    public function create($name, $description = '', $ruleName = null, $data = null): void
    {
        $am = $this->manager;
        if ($permission = $am->getPermission($name)) {
            throw new DomainException('Permission "' . $name . '" is already exist.');
        }
        $newPermission = $am->createPermission($name);
        $newPermission->description = $description;
        $newPermission->data = $data == null ? null : Json::decode($data);
        $newPermission->ruleName = empty($ruleName) ? null : $ruleName;
        try {
            $am->add($newPermission);
        }
        catch (ReflectionException|Exception $e)
        {
            throw new DomainException($e->getMessage());
        }
    }

    public function update($name, $newName, $description = '', $ruleName = null, $data = null): void
    {
        $am = $this->manager;
        if (!$permission = $am->getPermission($name)) {
            throw new DomainException('Permission "' . $name . '" does not exist.');
        }
        $permission->name = $newName;
        $permission->description = $description;
        $permission->ruleName = empty($ruleName) ? null : $ruleName;
        $permission->data = $data == null ? null : Json::decode($data);
        try {
            $am->update($name, $permission);
        }
        catch (ReflectionException|Exception $e) {
            throw new DomainException($e->getMessage());
        }
    }

    public function delete($name): void
    {
        $am = $this->manager;
        if (!$permission = $am->getPermission($name)) {
            throw new DomainException('Permission "' . $name . '" does not exist.');
        }
        $am->remove($permission);
    }

    /**
     * @throws \yii\base\Exception
     */
    public function assign($roleName, $permissionName): void
    {
        $am = $this->manager;
        if (!$role = $am->getRole($roleName)) {
            throw new DomainException('Role "' . $roleName . '" does not exist.');
        }
        if (!$permission = $am->getPermission($permissionName)) {
            throw new DomainException('Permission "' . $permissionName . '" does not exist.');
        }
        $am->addChild($role, $permission);
    }

    public function unassign($roleName, $permissionName): void
    {
        $am = $this->manager;
        if (!$role = $am->getRole($roleName)) {
            throw new DomainException('Role "' . $roleName . '" does not exist.');
        }
        if (!$permission = $am->getPermission($permissionName)) {
            throw new DomainException('Permission "' . $permissionName . '" does not exist.');
        }
        if (!$am->hasChild($role, $permission)) {
            throw new DomainException('Permission "' . $permissionName . '" does not assigned to "' . $roleName . '".');
        }
        $am->removeChild($role, $permission);
    }

    public function getPermissions(): array
    {
        $am = $this->manager;
        return $am->getPermissions();
    }

    public function getPermission($name): Permission
    {
        $am = $this->manager;
        if (!$permission = $am->getPermission($name)) {
            throw new DomainException('Permission "' . $name . '" does not exist.');
        }
        return $permission;
    }

    public function permissionExists($name): bool
    {
	    $am = $this->manager;
	    if (!$permission = $am->getPermission($name)) {
		    return false;
	    }
	    return true;
    }

    public function getPermissionsSelectArray(): array
    {
        return ArrayHelper::map($this->getPermissions(), 'name', 'description');
    }

    public function getPermissionsSelectArrayByRole($id): array
    {
        $am = $this->manager;
        return ArrayHelper::getColumn($am->getPermissionsByRole($id), 'name');
    }
}